package org.xzheng.repayment.service.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.joda.time.Days;
import org.xzheng.repayment.entity.ReplayPlan;
import org.xzheng.repayment.service.IRepayPlanGenerator;
import org.xzheng.repayment.util.DateUtil;

/**
 * 按月付息，到期还本.
 * 
 * <pre>
 每月支付的利息=本金×年利率÷365×持有天数
 * </pre>
 */
public class RePayAlgorithmMonth implements IRepayPlanGenerator {

	@Override
	public Map<Integer, ReplayPlan> getRepayPlan(double invest, double yearRate, int totalmonth, Date begin) {
		Map<Integer, ReplayPlan> map = new HashMap<Integer, ReplayPlan>();
		DateTime before = null;
		DateTime after = null;
		// 需求：当日作为计息开始日，计息结束日期为下个月当天的前一日。
		// 算法：开始日期先减1天，以次为基线，循环在月份上加上期数。月份的加法使用数据库算法。
		// 解释：
		// 1.月份加法数据库算法和java算法差异：9-30加1月，数据库算法结果是10-31，java算法结果是10-30。
		// 对java算法进行改造，加月份前先加1天再加月份最后再减1天

		// 2。示例
		// 计息开始日：8-30、8-31、9-1
		// A.先加月份再减1天: 加1月9-30、9-30、10-1，减1天9-29、9-29、9-30。
		// B.先减1天再加月份: 减1天8-29、8-30、8-31，加1月9-29、9-30、9-30。
		// 计息开始日：9-29、9-30、10-1
		// C.先加月份再减1天: 加1月10-29、10-31、11-1，减1天10-28、10-30、10-31。
		// D.先减1天再加月份: 减1天9-28、9-29、9-30，加1月10-28、10-29、10-31。
		// 其中:
		// 示例A中计息开始日8-31的计息结束日为9-29，少了1天
		// 示例C中计息开始日9-30计息结束日为10-30，多了1天
		// 先减1天再加月份，算法比较合理

		// 3.以开始日减一天作为基线，循环在月份上加上期数，如果有大小月情况出现，误差都是一致的。如果每期都是在上期的结果上减1天再加月份，每期都有可能出现误差。
		DateTime beginBase = new DateTime(begin);
		beginBase = beginBase.plusDays(-1);
		for (int i = 0; i < totalmonth; i++) {
			int period = i + 1;

			if (after == null) {
				before = new DateTime(begin);
			} else {
				before = after.plusDays(1);
			}

			after = new DateTime(DateUtil.add_month(beginBase.toDate(), period));

			int days = Days.daysBetween(before, after).getDays() + 1;
			BigDecimal interest = new BigDecimal(invest)
					.multiply(new BigDecimal(yearRate).multiply(new BigDecimal(days)));
			interest = interest.divide(new BigDecimal(365), 2, BigDecimal.ROUND_HALF_UP);

			BigDecimal bprincipal = new BigDecimal(0);
			if (period == totalmonth) {
				bprincipal = new BigDecimal(invest);
			}

			ReplayPlan plan = new ReplayPlan(period, yearRate, interest, bprincipal, before.toDate(), after.toDate(),
					days);

			map.put(period, plan);
		}
		return map;
	}

	public static void main(String[] args) throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date begin = sdf.parse("2017-07-21");
		Map<Integer, ReplayPlan> detailPlan = new RePayAlgorithmMonth().getRepayPlan(3000, 0.1238, 3, begin);
		System.out.println("按月付息到期还本");
		for (int i = 1; i <= detailPlan.size(); i++) {
			ReplayPlan plan = detailPlan.get(i);
			System.out.println(plan);
		}
	}
}
